﻿//Copyright (C) 2009  Flaming Idiot & Jaco (ScionBot.com)

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Scion.Controls
{
    public class TabStrip : System.Windows.Forms.Control
    {
        public TabStrip()
        {
            this.DoubleBuffered = true;
            this.SizeChanged += Invalidate;
            this.MouseUp += Invalidate;
            this.MouseDown += Invalidate;
            this.SetStyle(ControlStyles.StandardDoubleClick, false);
            this.Height = 21;
            this.Dock = DockStyle.Top;
            this.Font = new Font(this.Font, FontStyle.Bold);
        }

        private Form cutoffform = null;
        private Form _parentForm = null;
        private bool Overflow = false;
        private int offset = 0;
        private Point? mousePosition = null;

        public Form ParentForm
        {
            get
            {
                var form = this.FindForm();
                if (_parentForm != form)
                {
                    if (_parentForm != null)
                    {
                        _parentForm.MdiChildActivate -= form_MdiChildActivate;
                    }
                    if (form != null)
                    {
                        form.MdiChildActivate += form_MdiChildActivate;
                        MdiClient mdiClient = null;
                        foreach (var item in form.Controls)
                            if (item is MdiClient) mdiClient = item as MdiClient;
                        if (mdiClient != null)
                        {
                            mdiClient.BackColor = Color.FromArgb(70, 70, 70);

                            int style = External.API.GetWindowLong(mdiClient.Handle, -16);
                            int exStyle = External.API.GetWindowLong(mdiClient.Handle, -20);
                            style &= ~0x00800000;
                            exStyle &= ~0x00000200;
                            External.API.SetWindowLong(mdiClient.Handle, -16, style);
                            External.API.SetWindowLong(mdiClient.Handle, -20, exStyle);
                            External.API.SetWindowPos(mdiClient.Handle, IntPtr.Zero, 0, 0, 0, 0, 0x0010 | 0x0002 | 0x0001 | 0x0004 | 0x0200 | 0x0020);
                        }
                    }
                    _parentForm = form;
                }
                return _parentForm;
            }
        }

        void form_MdiChildActivate(object sender, EventArgs e)
        {
            var child = ParentForm.ActiveMdiChild;
            if (child != null)
                if (!(child is Bot))
                    child.ControlBox = false;
            BringWindowIntoView(ParentForm.ActiveMdiChild);
        }

        private void BringWindowIntoView(Form form)
        {
            if (form == null)
            { Invalidate(); return; }
            var tabs = MeasureTabs();
            if (!tabs.ContainsKey(form))
            {
                offset = 0;
                while (true)
                {
                    tabs = MeasureTabs();
                    if (tabs.ContainsKey(form)) break;
                    offset++;
                }
            }
            if (form == cutoffform)
                offset++;
            Invalidate();
        }

        private void Invalidate(object sender, EventArgs e)
        {
            Invalidate();
        }

        private Dictionary<Form, Rectangle> MeasureTabs()
        {
            if (ParentForm == null) return null;
            cutoffform = null;
            int left = 1;
            var graphics = this.CreateGraphics();
            Dictionary<Form, Rectangle> results = new Dictionary<Form, Rectangle>();
            foreach (var window in ParentForm.MdiChildren)
            {
                SizeF measure = graphics.MeasureString(window.Text, this.Font);
                measure.Width += 24;
                results.Add(window, new Rectangle(left, 1, (int)measure.Width, this.Height - 2));
                left += (int)measure.Width + 1;
            }
            Overflow = false;
            if (left > Width && results.Count > 1)
            {
                results.Clear();
                Rectangle b = this.ClientRectangle;
                b.Inflate(-16, -1);

                left = 16;
                int c = 0;
                foreach (var window in ParentForm.MdiChildren)
                {
                    if (c++ < offset) continue;
                    if (left >= b.Right) break;
                    SizeF measure = graphics.MeasureString(window.Text, this.Font);
                    measure.Width = (float)Math.Ceiling(measure.Width);
                    measure.Width += 24;
                    Rectangle tabBounds = Rectangle.Intersect(new Rectangle(left, 1, (int)measure.Width, this.Height - 2), b);
                    if (tabBounds.Width < measure.Width)
                        cutoffform = window;
                    if (tabBounds.Width > Height - 2) results.Add(window, tabBounds);
                    left += (int)measure.Width + 1;
                }
                Overflow = true;
            }

            return results;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (Height == 0 || Width == 0) return;

            var bounds = ClientRectangle;
            bounds.Inflate(-1, -1);

            e.Graphics.SmoothingMode = SmoothingMode.None;
            using (var Back = new SolidBrush(Color.FromArgb(51, 51, 51)))
            using (var XPen1 = new Pen(Color.FromArgb(80, 0, 0, 0)))
            using (var XPen2 = new Pen(Color.FromArgb(51, 51, 51), 1.5f))
            using (var BackgroundBrush = new LinearGradientBrush(new Point(), new Point(0, this.Height), Color.FromArgb(156, 156, 156), Color.FromArgb(135, 135, 135)))
            using (var HoverBrush = new LinearGradientBrush(new Point(), new Point(0, this.Height), Color.FromArgb(174, 174, 174), Color.FromArgb(152, 152, 152)))
            using (var SelectedBrush = new LinearGradientBrush(new Point(), new Point(0, this.Height), Color.FromArgb(235, 235, 235), Color.FromArgb(216, 216, 216)))
            using (var BorderPen = new Pen(Color.FromArgb(40, 255, 255, 255), 1.0f))
            using (var Format = new StringFormat() { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Center, FormatFlags = StringFormatFlags.NoWrap })
            {
                e.Graphics.FillRectangle(Back, 0, 0, Width, Height);

                if (Overflow) bounds.Inflate(-15, 0);

                e.Graphics.FillRectangle(BackgroundBrush, bounds);
                e.Graphics.DrawRectangle(BorderPen, bounds.Left, bounds.Top, bounds.Width - 1, bounds.Height - 1);

                var tabRects = MeasureTabs();
                if (tabRects == null) return;

                if (Overflow)
                {
                    Dictionary<Rectangle, string> btns = new Dictionary<Rectangle, string>()
                    {
                        { new Rectangle(1, 1, 14, bounds.Height), "-" },
                        { new Rectangle(Width - 15, 1, 14, bounds.Height), "+" }
                    };

                    foreach (var item in btns)
                    {
                        var btnBounds = item.Key;
                        bool isPressed = false;
                        e.Graphics.FillRectangle(Back, btnBounds.Left - 1, btnBounds.Top, btnBounds.Width + 2, btnBounds.Height);
                        bool isEnabled = true;

                        switch (item.Value)
                        {
                            case "+":
                                isEnabled = offset < ParentForm.MdiChildren.Length - 1 && cutoffform != null;
                                break;
                            case "-":
                                isEnabled = offset > 0;
                                break;
                        }

                        if (mousePosition.HasValue && btnBounds.Contains(mousePosition.Value) & isEnabled)
                        {
                            if (Control.MouseButtons != MouseButtons.None)
                            {
                                e.Graphics.FillRectangle(SelectedBrush, btnBounds);
                                e.Graphics.DrawRectangle(BorderPen, btnBounds.Left, btnBounds.Top, btnBounds.Width - 1, btnBounds.Height - 1);
                                isPressed = true;
                            }
                            else
                            {
                                e.Graphics.FillRectangle(HoverBrush, btnBounds);
                                e.Graphics.DrawRectangle(BorderPen, btnBounds.Left, btnBounds.Top, btnBounds.Width - 1, btnBounds.Height - 1);
                            }
                        }
                        else
                        {
                            e.Graphics.FillRectangle(BackgroundBrush, btnBounds);
                            e.Graphics.DrawRectangle(BorderPen, btnBounds.Left, btnBounds.Top, btnBounds.Width - 1, btnBounds.Height - 1);
                        }

                        using (var Bright = new Pen(Color.FromArgb(isPressed ? 128 : 30, 255, 255, 255)))
                        {
                            var loc = btnBounds.Location;
                            switch (item.Value)
                            {
                                case "+":
                                    e.Graphics.DrawLines(XPen2, new Point[] { loc + new Size(5, 5), loc + new Size(9, 9), loc + new Size(5, 13) });
                                    e.Graphics.DrawLines(Bright, new Point[] { loc + new Size(5, 6), loc + new Size(9, 10), loc + new Size(5, 14) });
                                    break;
                                case "-":
                                    e.Graphics.DrawLines(XPen2, new Point[] { loc + new Size(8, 5), loc + new Size(4, 9), loc + new Size(8, 13) });
                                    e.Graphics.DrawLines(Bright, new Point[] { loc + new Size(8, 6), loc + new Size(4, 10), loc + new Size(8, 14) });
                                    break;
                            }
                        }
                    }
                }

                foreach (var item in tabRects)
                {
                    e.Graphics.SmoothingMode = SmoothingMode.None;
                    Form window = item.Key;
                    Rectangle rect = item.Value;
                    bool isSelected = ParentForm.ActiveMdiChild == window;
                    e.Graphics.FillRectangle(Back, rect.Left, rect.Top, rect.Width + 1, rect.Height);
                    if (ParentForm.ActiveMdiChild == window)
                    {
                        e.Graphics.FillRectangle(SelectedBrush, rect);
                        e.Graphics.DrawRectangle(BorderPen, rect.Left, rect.Top, rect.Width - 1, rect.Height - 1);
                    }
                    else if (mousePosition.HasValue && rect.Contains(mousePosition.Value))
                    {
                        e.Graphics.FillRectangle(HoverBrush, rect);
                        e.Graphics.DrawRectangle(BorderPen, rect.Left, rect.Top, rect.Width - 1, rect.Height - 1);
                    }
                    else
                    {
                        e.Graphics.FillRectangle(BackgroundBrush, rect);
                        e.Graphics.DrawRectangle(BorderPen, rect.Left, rect.Top, rect.Width - 1, rect.Height - 1);
                    }

                    using (var Bright = new SolidBrush(Color.FromArgb(isSelected ? 128 : 30, 255, 255, 255)))
                    {
                        e.Graphics.DrawString(window.Text, this.Font, Bright, new RectangleF(rect.Left + 3, rect.Top + 1, rect.Width - 18, rect.Height), Format);
                    }
                    e.Graphics.DrawString(window.Text, this.Font, Back, new RectangleF(rect.Left + 3, rect.Top, rect.Width - 18, rect.Height), Format);

                    var pos = rect.Right - 16;
                    var top = rect.Top + (int)Math.Ceiling((rect.Height - 12.0) / 2.0);

                    var xrect = new Rectangle(pos, top, 12, 12);

                    if (mousePosition.HasValue && xrect.Contains(mousePosition.Value))
                    {
                        e.Graphics.DrawRectangle(XPen1, xrect.Left, xrect.Top, xrect.Width - 1, xrect.Height - 1);
                    }

                    xrect.Inflate(-2, -2);

                    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
                    e.Graphics.DrawLine(XPen2, xrect.Left, xrect.Top, xrect.Right - 1, xrect.Bottom - 1);
                    e.Graphics.DrawLine(XPen2, xrect.Right - 1, xrect.Top, xrect.Left, xrect.Bottom - 1);
                }
            }
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            var tabRects = MeasureTabs();
            if (tabRects == null) return;
            if (Overflow)
            {
                var bounds = ClientRectangle;
                bounds.Inflate(-1, -1);
                Dictionary<Rectangle, string> btns = new Dictionary<Rectangle, string>()
                    {
                        { new Rectangle(1, 1, 14, bounds.Height), "-" },
                        { new Rectangle(Width - 15, 1, 14, bounds.Height), "+" }
                    };
                foreach (var item in btns)
                {
                    if (item.Key.Contains(e.Location))
                    {
                        switch (item.Value)
                        {
                            case "+":
                                if (offset < ParentForm.MdiChildren.Length - 1 && cutoffform != null) offset += 1;
                                break;
                            case "-":
                                if (offset > 0) offset -= 1;
                                break;
                        }
                        return;
                    }
                }
            }
            foreach (var item in tabRects)
            {
                Form window = item.Key;
                Rectangle rect = item.Value;
                if (rect.Contains(e.Location))
                {
                    var pos = rect.Right - 16;
                    var top = rect.Top + (int)Math.Ceiling((rect.Height - 12.0) / 2.0);
                    var xrect = new Rectangle(pos, top, 12, 12);
                    if (xrect.Contains(e.Location))
                    {
                        window.Close();
                    }
                    else
                    {
                        window.Activate();
                    }
                }
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            mousePosition = e.Location;
            Invalidate();
            base.OnMouseMove(e);
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            mousePosition = null;
            Invalidate();
            base.OnMouseLeave(e);
        }
    }
}